package com.winning.pmph.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.winning.pmph.entity.*;
import com.winning.pmph.mapper.ExerciseGroupLibMapper;
import com.winning.pmph.utils.Const;
import com.winning.pmph.utils.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@Transactional
public class ExerciseGroupLibService extends ServiceImpl<ExerciseGroupLibMapper, ExerciseGroupLib> {

    @Resource
    private ExerciseEntryLibService exerciseEntryLibService;
    @Resource
    private ExamOutlineDetailService examOutlineDetailService;
    @Resource
    private ExamOutlineService examOutlineService;

    public void saveOrUpdateExerciseGroupLib(ExerciseGroupLib exerciseGroupLib) {
        // case_alias 与 entry 保持一致;
        if (StringUtils.equals(exerciseGroupLib.getRole(), ExerciseGroupLib.ROLE_A1)
                || StringUtils.equals(exerciseGroupLib.getRole(), ExerciseGroupLib.ROLE_B)) {
            String caseAlias = exerciseGroupLib.getExerciseEntryList().stream().map(ExerciseEntry::getQuestion).collect(Collectors.joining(System.lineSeparator()));
            exerciseGroupLib.setCaseAlias(caseAlias);
        }
        // B型题, 共用选项, 以第一题为准;
        if (StringUtils.equals(exerciseGroupLib.getRole(), ExerciseGroupLib.ROLE_B)) {
            exerciseGroupLib.getExerciseEntryList().forEach(exerciseEntry -> exerciseEntry.setChoiceArray(exerciseGroupLib.getExerciseEntryList().get(0).getChoiceArray()));
        }
        // 检查相似性;
        checkConflictGroup(exerciseGroupLib);
        saveOrUpdate(exerciseGroupLib);
        exerciseGroupLib.getExerciseEntryList().forEach(item -> {
            item.setGroupId(exerciseGroupLib.getId());
            item.setRole(exerciseGroupLib.getRole());
        });
        // 保存 entityList;
        exerciseEntryLibService.saveOrUpdateBatch(JSON.parseArray(JSON.toJSONString(exerciseGroupLib.getExerciseEntryList()), ExerciseEntryLib.class));
        // 新增或修改完之后,重新计算大纲和要点对应的题数,新增或修改时,关联大纲可能修改,可能不关联大纲,所以必须重新计算
        List<ExamOutlineDetail> detailList = examOutlineDetailService.lambdaQuery().list();
        List<ExerciseGroupLib> groupLibList = lambdaQuery().like(ExerciseGroupLib::getExamDetailIdArray, "[\"").list();
        // 设置关联总题数
        Map<String, ExerciseGroupLib> distinctDetailIdGroupLibMap = new HashMap<>();
        groupLibList.forEach(groupLibItem -> {
            groupLibItem.getExamDetailIdArray().forEach(detailIdItem -> {
                distinctDetailIdGroupLibMap.put(detailIdItem.toString(), groupLibItem);
            });
        });
        Map<String, Long> outlineExerciseNumMap = new HashMap<>();
        // 设置要点关联题数, 同时计算大纲关联题数
        detailList.forEach(item -> {
            item.setExerciseNum(groupLibList.stream().filter(groupItem -> groupItem.getExamDetailIdArray().contains(item.getId())).count());
            if (Objects.nonNull(distinctDetailIdGroupLibMap.get(item.getId()))) {
                long outlineExerciseNum = outlineExerciseNumMap.getOrDefault(item.getExamOutlineId(), 0L);
                outlineExerciseNum++;
                outlineExerciseNumMap.put(item.getExamOutlineId(), outlineExerciseNum);
            }
        });

        Map<String, ExamOutline> examOutlineMap = examOutlineService.lambdaQuery().eq(ExamOutline::getImportStatus, Const.IMPORT_STATUS_SUCCESS).list().stream()
                .collect(Collectors.toMap(ExamOutline::getId, Function.identity()));

        for (Map.Entry<String, Long> entry : outlineExerciseNumMap.entrySet()) {
            ExamOutline examOutline = examOutlineMap.get(entry.getKey());
            examOutline.setExerciseNum(entry.getValue());
            examOutlineService.updateById(examOutline);
        }
        examOutlineDetailService.updateBatchById(detailList);
    }

    private void checkConflictGroup(ExerciseGroupLib exerciseGroupLib) {
        // 先把当前实体冲突置为解决;
        if (StringUtils.isNotEmpty(exerciseGroupLib.getConflictId())) {
            List<ExerciseGroupLib> exerciseGroupLibList = lambdaQuery().eq(ExerciseGroupLib::getConflictId, exerciseGroupLib.getConflictId()).list();
            // 如果当前试题的相似试题组个数小于3(1,2), 则把试题组冲突ID置为空;
            if (exerciseGroupLibList.size() < 3) {
                exerciseGroupLibList.forEach(item -> item.setConflictId(StringUtils.EMPTY));
                updateBatchById(exerciseGroupLibList);
            }
            // 如果相似试题组个数大于2, 则只需要把当前试题的冲突ID置为空, 其它冲突试题还在一个组内;
            else {
                exerciseGroupLib.setConflictId(StringUtils.EMPTY);
                updateById(exerciseGroupLib);
            }
        }
        // 查询当前类型所有试题caseAlias, 根据相似性过滤;
        List<ExerciseGroupLib> dbExerciseGroupLibList = lambdaQuery()
                .eq(ExerciseGroupLib::getRole, exerciseGroupLib.getRole()).list();
        String exerciseEntityListStr = exerciseGroupLib.getExerciseEntryList().stream().map(item -> item.getQuestion() + item.getChoiceArray()).collect(Collectors.joining());
        for (ExerciseGroupLib dbExerciseGroupLib : dbExerciseGroupLibList) {
            if (StringUtils.equals(dbExerciseGroupLib.getId(), exerciseGroupLib.getId())
                || StringUtils.equals(dbExerciseGroupLib.getConflictId(), exerciseGroupLib.getConflictId())) {
                continue;
            }
            // caseAias相似;
            if (StringUtil.jaccard(dbExerciseGroupLib.getCaseAlias(), exerciseGroupLib.getCaseAlias()) > Const.SIMILAR_FACTOR) {
                // entity也相似;
                List<ExerciseEntryLib> dbExerciseEntryList = exerciseEntryLibService.lambdaQuery().eq(ExerciseEntryLib::getGroupId, dbExerciseGroupLib.getId()).list();
                String dbEntityStr = dbExerciseEntryList
                        .stream().map(item -> item.getQuestion() + item.getChoiceArray()).collect(Collectors.joining());
                if (StringUtil.jaccard(exerciseEntityListStr, dbEntityStr) > Const.SIMILAR_FACTOR) {
                    setConflict(exerciseGroupLib, dbExerciseGroupLib, dbExerciseGroupLibList);
                }
            }
        }
    }

    private void setConflict(ExerciseGroupLib exerciseGroupLib, ExerciseGroupLib dbExerciseGroupLib, List<ExerciseGroupLib> dbExerciseGroupLibList) {
        // 当前实体冲突id为空, 以数据库实体为主;
        if (StringUtils.isEmpty(exerciseGroupLib.getConflictId())) {
            if (StringUtils.isEmpty(dbExerciseGroupLib.getConflictId())) {
                dbExerciseGroupLib.setConflictId(IdWorker.getIdStr());
                updateById(dbExerciseGroupLib);
            }
            exerciseGroupLib.setConflictId(dbExerciseGroupLib.getConflictId());
            updateById(exerciseGroupLib);
        }
        // 当前实体冲突id不为空, 如果数据库实体冲突ID不为空, 则统一修改多个实体;
        else if (StringUtils.isNotEmpty(dbExerciseGroupLib.getConflictId())) {
            List<ExerciseGroupLib> crtConflictList = dbExerciseGroupLibList.stream()
                    .filter(item -> StringUtils.equals(item.getConflictId(), dbExerciseGroupLib.getConflictId()))
                    .collect(Collectors.toList());
            crtConflictList.forEach(item -> item.setConflictId(exerciseGroupLib.getConflictId()));
            updateBatchById(crtConflictList);
        }
        // 当前实体冲突id不为空, 数据库实体冲突ID为空, 只修改数据库实体;
        else {
            dbExerciseGroupLib.setConflictId(exerciseGroupLib.getConflictId());
            updateById(dbExerciseGroupLib);
        }
    }

    public Page<ExerciseGroupLib> selectExerciseGroupLibPage(ExerciseGroupLib exerciseGroupLib) {
        Page<ExerciseGroupLib> page = new Page<>(exerciseGroupLib.getCurrentPage(), exerciseGroupLib.getPageSize());
        LambdaQueryWrapper<ExerciseGroupLib> queryWrapper = Wrappers.lambdaQuery(ExerciseGroupLib.class);
        if (StringUtils.isNotEmpty(exerciseGroupLib.getRole())) {
            queryWrapper.eq(ExerciseGroupLib::getRole, exerciseGroupLib.getRole());
        }
        if (StringUtils.isNotEmpty(exerciseGroupLib.getCaseAlias())) {
            queryWrapper.like(ExerciseGroupLib::getCaseAlias, exerciseGroupLib.getCaseAlias());
        }
        if (exerciseGroupLib.isConflictFlag()) {
            queryWrapper.ne(ExerciseGroupLib::getConflictId, StringUtils.EMPTY).orderByDesc(ExerciseGroupLib::getConflictId);
        }
        // 暂时不考虑一个要点下面对应超过1000道题
        if (StringUtils.isNotBlank(exerciseGroupLib.getExamDetailId())) {
            queryWrapper.like(ExerciseGroupLib::getExamDetailIdArray, exerciseGroupLib.getExamDetailId());
        }
        queryWrapper.orderByDesc(ExerciseGroupLib::getId);
        return page(page, queryWrapper);
    }

    public void deleteExerciseGroupLib(ExerciseGroupLib exerciseGroupLib) {
        exerciseEntryLibService.remove(Wrappers.lambdaQuery(ExerciseEntryLib.class)
                .eq(ExerciseEntryLib::getGroupId, exerciseGroupLib.getId()));
        removeById(exerciseGroupLib.getId());
        // 判断类似试题;
        if (StringUtils.isNotEmpty(exerciseGroupLib.getConflictId())) {
            List<ExerciseGroupLib> exerciseGroupLibList = lambdaQuery().eq(ExerciseGroupLib::getConflictId, exerciseGroupLib.getConflictId()).list();
            if (exerciseGroupLibList.size() == 1) {
                update(Wrappers.lambdaUpdate(ExerciseGroupLib.class)
                        .set(ExerciseGroupLib::getConflictId, StringUtils.EMPTY)
                        .eq(ExerciseGroupLib::getConflictId, exerciseGroupLib.getConflictId()));
            }
        }
        // 删除时,修改大纲和要点对应的题数
        if (exerciseGroupLib.getExamDetailIdArray().size() > 0) {
            List<ExamOutlineDetail> detailList = examOutlineDetailService.lambdaQuery().in(ExamOutlineDetail::getId, JSONArray.parseArray(JSON.toJSONString(exerciseGroupLib.getExamDetailIdArray()), String.class)).list();
            detailList.forEach(detailItem -> {
                detailItem.setExerciseNum(detailItem.getExerciseNum() - 1);
            });
            // 修改大纲
            List<ExamOutline> outlineList = examOutlineService.lambdaQuery().in(ExamOutline::getId, detailList.stream().map(ExamOutlineDetail::getExamOutlineId).collect(Collectors.toSet())).list();
            outlineList.forEach(outlineItem -> {
                outlineItem.setExerciseNum(outlineItem.getExerciseNum() - 1);
            });
            examOutlineService.updateBatchById(outlineList);
            examOutlineDetailService.updateBatchById(detailList);
        }
    }
}
